Skip to main content

Performance tips

Performance tips

You can explicitly choose what will be interpreted on the frontend or backend. We have a few possibilities for our function inside Line expression

All load to Kernel

For this one need to change code to

EventHandler[InputRange[0,4,0.1], Function[data, 
lines = With[{y = data},
Table[{Cos[x], Sin[y x]}, {x,0,2Pi, 0.01}]
]
]];
% // EventFire (* just to initialize *)

The last line manually fires an event to initialize symbol lines. Then for the output we can write

Graphics[{Cyan, Line[lines // Offload]}]

One can illustrate this binding as on a picture below

Using frontend

One can move an entire Table to the browser's side. Let's discard our changes we made to

EventHandler[InputRange[0,4,0.1], Function[data, 
v = data
]];
% // EventFire
Naive approach 1

The obvious solution for output could be

Graphics[{Cyan, Line[
Table[{Cos[x], Sin[Offload[v] x]}, {x,0,2$Pi$, 0.1}]
]}]

That will be a horrible solution 👎🏼

Imagine, each time Table iterator x goes through the range of values, it creates a sublist of Sin and Cos functions, that contains dynamic variable v. Then you end up with many instances of v.

danger
Line[Table[Expression[Offload[symbol]], {i, 10}]]

Creates 10 instances of symbol. Line function will be called 10 times on each update of symbol!

danger

Do not put dynamic symbols inside large Table. Try to minimize the number of its copies made.

Naive approach 2

Ok lets try to improve a bit

Graphics[{Cyan, Line[
Table[{Cos[x], Sin[v x]}, {x,0,2Pi, 0.1}] // Offload
]}]

This is also horrible 👎🏼 Symbol Table does the same thing being executed on browser's side as well

Optimized version

One can minimize the number of instances to just 1 using With, as it was shown in the example above

Graphics[{Cyan, Line[
With[{y = v},
Table[{Cos[x], Sin[y x]}, {x,0,2Pi, 0.01}]
] // Offload
]
}]

This will save up a lot of resources 👍🏼

tip
Line[With[{y = symbol}, Table[AnyExpression[y], {i, 10}]]]

Creates only 1 instance of symbol. A Line function will be called 1 time per update of a symbol.

tip
Line[symbol//Offload], ... Line[symbol//Offload]

This is ok, each Line is bounded to its own symbol instance. Therefore on update of symbol, each Line expression will be reevaluated once.

If duplicating is unavoidable

If you have to update two properties of a dynamic expression such as GraphicsComplex which are VertexColors and list of vertices for this example, it is unavoidable to use two Offload s there

GraphicsComplex[vertices // Offload, {Polygon[triangles]}, "VertexColors"->Offload[colors]]

then if later in the code

vertices = ...;
colors = ...;

will cause the reevaluation of GraphicsComplex two times for the same set of data. However, there is a way on how to suppress the second one using options of Offload

GraphicsComplex[vertices // Offload, {Polygon[triangles]}, "VertexColors"->Offload[colors, "Static"->True]]

Here colors will not be bounded to GraphicsComplex. That results in only a single reevaluation per updates of colors and vertices. However a new values is going to be read anyway once vertices has been updated.

Possible pitfall with With

There might be temptation to wrap Line expression inside With as well, like that

Graphics[{Cyan, With[{y = v}, 
Line[
Table[{Cos[x], Sin[y x]}, {x,0,2Pi, 0.01}]
]
] // Offload}]

This will not work at all 👎🏼 because the binding will occur between Graphics and v objects

Think about an onion from the Shrek movie

Numeric arrays

When it goes to transfer any points as nested lists, it is better to wrap them into NumericArray. It tells WLJS Interpreter on the browser, that we can expect only numbers or lists of numbers, there which reduces the load while parsing them.

For example - dynamic symbols

(* every update *)
symbol = someFunctionThatReturnsList

20 FPS

then using NumericArray

(* every update *)
symbol = NumericArray[someFunctionThatReturnsList]

~40 FPS